#!/usr/bin/python3
# -*- coding: utf8 -*-
# Date   : 2023/04/28
# Author : Kwan
# Email  : allenxian@live.com
# Blog   : https://my.oschina.net/kwanxian

import casbin
from fastapi import HTTPException
from functools import wraps
from typing import List, Tuple
from json import loads, dumps
from flask import abort

# CURD 对照 get:读取, post:新增, put:更新, delete:删除
curds = {"GET":"get","POST":"post","PUT":"put","DELETE":"delete"}
# 加载权限模型
effect = casbin.Enforcer("model.conf", "policy.csv")
# 角色定义
roleFile = "role.json"


class Role:
    """
    - name: 角色名
    - desc: 角色描述
    """
    def __init__(self, **kwargs) -> None:
        self.name = ""
        self.desc = ""
        if kwargs:
            self.__dict__.update(**kwargs)
        # 用户列表
        self.users = []
        # 规则列表：[(sub, obj, act),]
        # * 表示所有
        self.rules = []
        # 规则列表(前端)
        self.perms = []
        # 初始化加载
        self.refresh_rules()

    def refresh_perms(self):
        """
        更新权限数据(前端)
        - [ {"name":str, "path":str, "perm":list} ]
        """
        self.perms = []
        for r in self.rules:
            sub, obj, act = r
            # GET > [get], (GET)|(POST) > [get,post]
            perm = [v for k, v in curds.items() if k in act]
            self.perms.append({"name":sub, "path":obj, "perm":perm})

    def refresh_rules(self, rules:list = []):
        """
        更新规则及权限
        - [['user', '/admin/', 'GET'], ['user', '/admin/user', '(GET)|(POST)']]
        """
        self.rules = rules
        if not rules:
            # 查询
            self.rules = effect.get_filtered_policy(0, self.name)
        if self.rules:
            self.refresh_perms()


class RoleManager:
    """
    角色权限管理
    """
    def __init__(self) -> None:
        self.roles = [Role(**i) for i in self.load_roles()]

    def load_roles(self):
        """
        加载角色
        """
        with open(file=roleFile, mode="r", encoding="utf8") as f:
            return loads(f.read())

    def save_roles(self):
        """
        保存角色
        """
        with open(file=roleFile, mode="w", encoding="utf8") as f:
            f.write(dumps([{"name":r.name,"desc":r.desc} for r in self.roles], ensure_ascii=False))

    def add_role(self, **kwargs):
        """
        新增角色
        """
        self.roles.append(Role(**kwargs))
        self.save_roles()
        return kwargs
    
    def del_role(self, name:str):
        """
        删除角色
        """
        for r in self.roles:
            if isinstance(r, Role) and r.name == name:
                if r.rules:
                    effect.remove_policies(r.rules)
                self.roles.remove(r)
                self.save_roles()
                return name

    def __to_action(self, act:List[str]):
        """
        规则策略转换
        - get > GET
        - get,post > (GET)|(POST)
        """
        act = [i.upper() for i in act]
        if len(act) == 1:
            # 最少1条权限
            return act[0]
        else:
            # (GET)|(POST)|(PUT)|(DELETE)
            return "|".join([f"({i})" for i in act])

    def mod_role(self, name:str, perms:List[Tuple[str, list]]):
        """
        更新角色权限
        - name: str,
        - perm: [(path, action),]
        """
        for r in self.roles:
            if isinstance(r, Role) and r.name == name:
                rules = [[name, p[0], self.__to_action(p[1]) ] for p in perms]
                if r.rules:
                    # 删除旧规则
                    effect.remove_policies(r.rules)
                # 写入新规则
                effect.add_policies(rules)
                # 保存规则
                effect.save_policy()
                # 刷新角色规则
                r.refresh_rules(rules)
                return r.perms
            
    def mod_user(self, user:str, role:str):
        """
        更新用户角色
        - 存在时删除
        - 不存在时新增
        """
        saved = False
        if effect.has_role_for_user(user, role):
            # 删除
            saved = effect.delete_role_for_user(user, role)
        else:
            # 新增
            saved = effect.add_role_for_user(user, role)
        if saved:
            effect.save_policy()
        return saved

def faster_validate(sub:str = "guest", obj:str = "/", act:str = "GET"):
    """
    权限验证装饰函数 - FastAPI
    - sub : 角色
    - obj : 资源
    - act : 操作
    """
    def decorator(f):
        @wraps(f)
        async def decorated(*args, **kwargs):
            if not effect.enforce(sub, obj, act):
                raise HTTPException(status_code=403, detail={"message":"access deny"})
            # 允许
            return f(*args, **kwargs)
        return decorated
    return decorator


def flasky_validate(sub:str = "guest", obj:str = "/", act:str = "GET"):
    """
    权限验证装饰函数 - Flasky
    - sub : 角色
    - obj : 资源
    - act : 操作
    """
    def decorator(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            if not effect.enforce(sub, obj, act):
                abort(403)
            # 允许
            return f(*args, **kwargs)
        return decorated
    return decorator


if __name__ == "__main__":
    manager = RoleManager()
    [print(r.perms) for r in manager.roles]